/*
 * @description 接口文档处理
 * @author: Rid King 
 * @since 2019-10-30 23:22:55 
 */

import 'xlsx/jszip.js'
import xlsx from 'xlsx'
import interBis from './interface.js'
import { strer, dater } from '@daelui/dogjs/dist/components.js'
// import xlsx2 from 'xlsx-style'

export default {
  leters: 'ABCDEFGHIJKLMNOPRSTUVWXYZ',
  // 数据列
  cols: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'],

  /**
   * @function 下载单sheet页表格
   * @param {Object} data 数据对象
  */
  loadSingleSheet (data) {
    // 接口列表处理
    data.interfaces = data.interfaces || []
    // 创建一个工作簿
    const workBook = xlsx.utils.book_new()
    // 添加接口列表总述,// 向工作簿追加一个工作表
    xlsx.utils.book_append_sheet(
      workBook,
      this.getInterListSheet(data),
      this.getModuleSheetName(data)
    )
    // 统计
    let state = this.statistic(data)
    let wsData = state.wsData || []
    let style = state.style || {}
    let merges = state.merges || []
    let rows = state.rows || []
    let rowCount = state.rowCount || 0
    // 列索引最大值
    let colsIndex = this.cols.length - 1
    // 遍历接口
    data.interfaces.forEach((item, i) => {
      // 数据列
      let cols = this.cols
      // 接口之间隔2行
      if (i) {
        wsData = wsData.concat([['************************************************分隔线************************************************']])
        rows = rows.concat([{hpt: 34}])
        rowCount += 1
        // 接口之间间隔行加背景
        cols.forEach(key => {
          style[key + (rowCount + 1)] = this.getBCCStyle(0, 'FF0000')
        })
        merges.push({s: {c: 0, r: rowCount}, e: {c: colsIndex, r: rowCount}})
      }
      // 接口信息
      let state = this.getInter(item, wsData, style, merges, rows, rowCount, i)
      wsData = state.wsData || []
      style = state.style || {}
      merges = state.merges || []
      rows = state.rows || []
      rowCount = state.rowCount || 0
    })
    // 使用二维数组创建一个工作表对象
    const workSheet = xlsx.utils.aoa_to_sheet(wsData)
    // 列宽度
    workSheet['!cols'] = [
      {wch: 18},
      {wch: 13},
      {wch: 13},
      {wch: 13},
      {wch: 13},
      {wch: 13},
      {wch: 13},
      {wch: 40},
      {wch: 13},
      {wch: 13},
    ]
    // 混合配置
    this.mixinOption(workSheet, rows, merges, style)
    // 向工作簿追加一个工作表
    xlsx.utils.book_append_sheet(workBook, workSheet, data.desc + '_' + data.module + '_接口详细')
    // 下载表格
    xlsx.writeFile(workBook, data.desc + '-' + data.module + '.xlsx')
  },

  /**
   * @function 下载多sheet页表格
   * @param {Object} data 数据对象
  */
  loadMultiSheet (data) {
    // 接口列表处理
    data.interfaces = data.interfaces || []
    // 接口sheet信息集合 
    let inters = []
    data.interfaces.forEach(item => {
      // 初始化接口sheet页名
      let node = this.getInterSheetInfo(item, inters)
      inters.push(node)
    })
    // 创建一个工作簿
    const workBook = xlsx.utils.book_new()
    // 添加接口列表总述,// 向工作簿追加一个工作表
    xlsx.utils.book_append_sheet(
      workBook,
      this.getInterListSheet(data, inters),
      this.getModuleSheetName(data)
    )
    // 遍历接口
    data.interfaces.forEach((item, i) => {
      let wsData = []
      let style = {}
      let merges = []
      let rows = []
      let rowCount = -1
      // 数据列
      let cols = this.cols
      // 接口信息
      let state = this.getInter(item, wsData, style, merges, rows, rowCount, undefined)
      wsData = state.wsData || []
      style = state.style || {}
      merges = state.merges || []
      rows = state.rows || []
      rowCount = state.rowCount || 0
      // 使用二维数组创建一个工作表对象
      const workSheet = xlsx.utils.aoa_to_sheet(wsData)
      // 列宽度
      workSheet['!cols'] = [
        {wch: 18},
        {wch: 13},
        {wch: 13},
        {wch: 13},
        {wch: 13},
        {wch: 13},
        {wch: 13},
        {wch: 40},
        {wch: 13},
        {wch: 13}
      ]
      // 混合配置
      this.mixinOption(workSheet, rows, merges, style)
      // 向工作簿追加一个工作表
      let name = inters[i].name
      // 返回列表
      let last = this.leters.split('')[cols.length]
      workSheet[last + '1'] = Object.assign({
        t: 's',
        v: '返回列表',
        l: {
          Target: '#' + this.getModuleSheetName(data) + '!A1'
        }
      }, this.getBCCStyle(false, '337AB7', false))
      xlsx.utils.book_append_sheet(workBook, workSheet, name)
    })
    // 下载表格
    xlsx.writeFile(workBook, data.desc + '-' + data.module + '.xlsx')
  },

  /**
   * @function 接口统计内容
   * @param {Object} data 数据对象
  */
  statistic (data) {
    // 列索引最大值
    let colsIndex = this.cols.length - 1
    // 模块信息
    let wsData = [
      [data.desc + '-' + data.module + '服务API接口文档'],
      ['说明：\r\n所有字符统一为 utf-8\r\n 注意：Y表示是  N表示否 \r\n' + (data.explain || '')],
      ['版本', '日期', '', '接口总数'],
      [data.serve.version || '', dater.format(new Date()), '', data.interfaces.length],
      ['接口列表']
    ]
    // 行记数
    let rowCount = wsData.length - 1
    // 合并的单元格配置
    let merges = [
      {s: {c: 0, r: 0}, e: {c: colsIndex, r: 0}}, // 标题
      {s: {c: 0, r: 1}, e: {c: colsIndex, r: 1}}, // 文档说明
      {s: {c: 1, r: 2}, e: {c: 2, r: 2}}, // 模块表格头
      {s: {c: 3, r: 2}, e: {c: colsIndex, r: 2}}, // 模块表格头
      {s: {c: 1, r: 3}, e: {c: 2, r: 3}}, // 模块表格行
      {s: {c: 3, r: 3}, e: {c: colsIndex, r: 3}}, // 模块表格行
      {s: {c: 0, r: 4}, e: {c: colsIndex, r: 4}} // 接口列表
    ]
    // 行高度
    let rows = [
      {hpt: 30}, {hpt: 60}, {}, {}, {hpt: 24}
    ]
    // 单元格样式
    let style = {}
    // 标题样式
    style['A1'] = this.getBCCStyle(12)
    // 文档说明样式
    style['A2'] = this.getBCCStyle(11, 'FF0000', false, 'left')
    // 模块说明表头样式
    style['A3'] = style['B3'] = style['D3'] =  this.getBCCStyle()
    // 模块说明内容样式
    style['A4'] = style['B4'] = style['D4'] =  this.getBCCStyle(11, '000000', false)
    // 模块说明内容样式
    style['A5'] = this.getBCCStyle(11, 'FF0000', false)

    return {wsData, style, merges, rows, rowCount}
  },

  /**
   * @function 下载接口模块数据至表格
   * @param {Object} item 接口对象
  */
  getInter (item, wsData, style, merges, rows, rowCount, i) {
    // 数据列
    let cols = this.cols
    // 列索引最大值
    let colsIndex = this.cols.length - 1
    /* 基本信息 */
    let base = [
      ['基本信息' + (isNaN(i) ? '' : '---序号：' + (i + 1)), '', '', '', '', '', '', '', '', '', '', ''],
      ['功能描述', item.desc || ''],
      ['请求地址', item.url || ''],
      ['请求类型', item.method || ''],
      ['请求格式', item.request.type || ''],
      ['响应格式', item.response.type || ''],
      ['开发者', item.developer || ''],
      ['详细说明', item.explain || '']
    ]
    // 基本信息样式
    style['A' + (rowCount + 2)] = this.getBLCFStyle()
    // 其余加粗
    for (let i = 1; i < base.length; i++) {
      style['A' + (rowCount + 2 + i)] = this.getBCCStyle(11, '000000', true, 'left')
      style['B' + (rowCount + 2 + i)] = this.getBCCStyle(11, '000000', false, 'left')
    }
    // 详细说明内容样式
    style['B' + (rowCount + base.length)] = this.getBCCStyle(11, '000000', false, 'left')
    // 合并
    let len = wsData.length
    merges = merges.concat([
      {s: {c: 0, r: len}, e: {c: colsIndex, r: len}}, // 基本信息
      {s: {c: 1, r: len + 1}, e: {c: colsIndex, r: len + 1}}, // 功能描述
      {s: {c: 1, r: len + 2}, e: {c: colsIndex, r: len + 2}}, // 请求地址
      {s: {c: 1, r: len + 3}, e: {c: colsIndex, r: len + 3}}, // 请求类型
      {s: {c: 1, r: len + 4}, e: {c: colsIndex, r: len + 4}}, // 请求格式
      {s: {c: 1, r: len + 5}, e: {c: colsIndex, r: len + 5}}, // 响应格式
      {s: {c: 1, r: len + 6}, e: {c: colsIndex, r: len + 6}}, // 开发者
      {s: {c: 1, r: len + 7}, e: {c: colsIndex, r: len + 7}} // 详细说明
    ])
    // 行高设置
    for (let i = 0; i < base.length; i++) {
      // 详细加高
      if (i === base.length - 1) {
        rows.push({hpt: 60})
      }
      else {
        rows.push({})
      }
    }
    // 基本信息行数
    rowCount += base.length

    /* 请求参数 */
    let request = [
      ['请求参数'],
      ['名称', '类型', '默认值', '字段描述', '是否必需', '是否校验', '校验规则', '备注']
    ]
    // 请求参数样式
    style['A' + (rowCount + 2)] = this.getBLCFStyle()
    // 参数标题样式
    cols.forEach(key => {
      let itemKey = key + (rowCount + 3)
      style[itemKey] = this.getBCCStyle()
    })
    // 合并
    merges = merges.concat([
      {s: {c: 0, r: rowCount + 1}, e: {c: colsIndex, r: rowCount + 1}} // 请求参数
    ])
    // 请求参数
    let reqParams = interBis.unPackParams(item.request.params || [])
    // 忽略的行数
    let ignoreCount = 0
    reqParams.forEach((node, i) => {
      let nodeRow = this.getParamRow(node, i, reqParams)
      if (!nodeRow) {
        ignoreCount -= 1
        return true
      }
      // 加入参数行
      request.push(nodeRow)
      // 参数样式
      cols.forEach((key, n) => {
        if (n) {
          let itemKey = key + (rowCount + 4 + i + ignoreCount)
          style[itemKey] = this.getBCCStyle(11, '000000', false)
        }
      })
    })
    // 请求与响应示例
    let defRequest = ''
    let defResponse = ''
    // 默认场景的请求与响应示例
    let sceneList = Array.isArray(item.scene) ? item.scene : []
    // 默认场景
    let scene = ''
    // 备选场景
    let spare = ''
    sceneList.forEach(node => {
      // 默认场景
      if (String(node.isAble) === '1') {
        if (String(node.isDefScene) === '1' && !scene) {
          scene = node
        }
        else if (!spare) {
          spare = node
        }
      }
    })
    // 无默认
    scene = scene || spare
    // 测试数据
    if (scene) {
      defRequest = scene.request || ''
      defResponse = scene.response || ''
    }

    /* 请求示例 */
    request = request.concat([
      ['请求示例'],
      [strer.pretty(defRequest)]
    ])
    // 行高设置
    for (let i = 0; i < request.length; i++) {
      // 示例高度加大
      if (i === request.length - 1) {
        rows.push({hpt: 120})
      }
      else {
        rows.push({})
      }
    }
    // 请求行数
    rowCount += request.length
    // 请求示例样式
    style['A' + (rowCount)] = this.getBLCFStyle()
    // 请求示例内容样式
    style['A' + (rowCount + 1)] = {
      s: {
        alignment: {
          horizontal: 'left',
          vertical: 'top'
        }
      }
    }
    // 合并
    merges = merges.concat([
      {s: {c: 0, r: rowCount - 1}, e: {c: colsIndex, r: rowCount - 1}}, // 请求示例
      {s: {c: 0, r: rowCount}, e: {c: colsIndex, r: rowCount}} // 请求示例内容
    ])

    /* 响应内容 */
    let response = [
      ['响应内容'],
      ['名称', '类型', '默认值', '字段描述', '是否必需', '是否校验', '校验规则', '备注']
    ]
    // 样式
    style['A' + (rowCount + 2)] = this.getBLCFStyle()
    // 参数标题样式
    cols.forEach(key => {
      let itemKey = key + (rowCount + 3)
      style[itemKey] = this.getBCCStyle()
    })
    // 合并
    merges = merges.concat([
      {s: {c: 0, r: rowCount + 1}, e: {c: colsIndex, r: rowCount + 1}} // 请求参数
    ])
    // 响应参数
    let resParams = interBis.unPackParams(item.response.params || [])
    // 重置忽略行
    ignoreCount = 0
    resParams.forEach((node, i) => {
      let nodeRow = this.getParamRow(node, i, resParams)
      if (!nodeRow) {
        ignoreCount -= 1
        return true
      }
      // 加入参数行
      response.push(nodeRow)
      // 参数样式
      cols.forEach((key, n) => {
        if (n) {
          let itemKey = key + (rowCount + 4 + i + ignoreCount)
          style[itemKey] = this.getBCCStyle(11, '000000', false)
        }
      })
    })

    /* 响应示例 */
    response = response.concat([
      ['响应示例'],
      [strer.pretty(defResponse)]
    ])
    // 行高设置
    for (let i = 0; i < response.length; i++) {
      // 示例高度加大
      if (i === response.length - 1) {
        rows.push({hpt: 120})
      }
      else {
        rows.push({})
      }
    }
    // 响应行数
    rowCount += response.length
    // 响应示例样式
    style['A' + (rowCount)] = this.getBLCFStyle()
    // 响应示例内容样式
    style['A' + (rowCount + 1)] = {
      s: {
        alignment: {
          horizontal: 'left',
          vertical: 'top'
        }
      }
    }
    // 合并
    merges = merges.concat([
      {s: {c: 0, r: rowCount - 1}, e: {c: colsIndex, r: rowCount - 1}}, // 响应示例
      {s: {c: 0, r: rowCount}, e: {c: colsIndex, r: rowCount}} // 响应示例内容
    ])

    /* 运行环境 */
    let env = []
    // 遍历环境
    let envList = item.env || []
    // 是否有参数
    let hasEnvParam = false
    envList.forEach((node, i) => {
      // 参数值
      let params = node.params || []
      if (params.length > 0) {
        hasEnvParam = true
      }
    })
    if (hasEnvParam) {
      env = [
        ['运行环境']
      ]
      rows.push({})
      let head = ['名称', '描述']
      envList.forEach((node, i) => {
        let row = []
        // 名称、描述
        row.push(node.serve)
        row.push(node.name)
        // 参数值
        let params = node.params || []
        params.forEach((sec, n) => {
          // 头
          if (!i) {
            head.push(sec.name || '')
          }
          // 行值
          row.push(sec.value || '')
        })
        // 头
        if (!i) {
          env.push(head)
          rows.push({})
        }
        // 添加行
        env.push(row)
        rows.push({})
      })
      // 运行环境样式
      style['A' + (rowCount + 2)] = this.getBLCFStyle()
      // 标题样式
      cols.forEach(key => {
        let itemKey = key + (rowCount + 3)
        style[itemKey] = this.getBCCStyle(11, '000000', true)
      })
      // 合并
      merges = merges.concat([
        {s: {c: 0, r: rowCount + 1}, e: {c: colsIndex, r: rowCount + 1}} // 运行环境
      ])
    }
    // 响应行数
    rowCount += env.length

    // 合并
    wsData = wsData.concat(base).concat(request).concat(response).concat(env)

    return {wsData, style, merges, rows, rowCount}
  },

  /**
   * @function 获取模块接口列表sheet
  */
  getInterListSheet (data, inters) {
    // 模块信息
    let wsData = [
      [data.desc + '-' + data.module + '服务API接口文档'],
      ['说明：\r\n所有字符统一为 utf-8\r\n 注意：Y表示是  N表示否 \r\n' + (data.explain || '')],
      ['版本', '日期', '', '接口总数'],
      [data.serve.version || '', dater.format(new Date()), '', data.interfaces.length],
      ['接口列表']
    ]
    let colsIndex = 7
    // 行记数
    let rowCount = wsData.length - 1
    // 合并的单元格配置
    let merges = [
      {s: {c: 0, r: 0}, e: {c: colsIndex, r: 0}}, // 标题
      {s: {c: 0, r: 1}, e: {c: colsIndex, r: 1}}, // 文档说明
      {s: {c: 1, r: 2}, e: {c: 2, r: 2}}, // 模块表格头
      {s: {c: 3, r: 2}, e: {c: colsIndex, r: 2}}, // 模块表格头
      {s: {c: 1, r: 3}, e: {c: 2, r: 3}}, // 模块表格行
      {s: {c: 3, r: 3}, e: {c: colsIndex, r: 3}}, // 模块表格行
      {s: {c: 0, r: 4}, e: {c: colsIndex, r: 4}} // 接口列表
    ]
    // 行高度
    let rows = [
      {hpt: 30}, {hpt: 60}, {}, {}, {hpt: 24}
    ]
    // 单元格样式
    let style = {}
    // 标题样式
    style['A1'] = this.getBCCStyle(12)
    // 文档说明样式
    style['A2'] = this.getBCCStyle(11, 'FF0000', false, 'left')
    // 模块说明表头样式
    style['A3'] = style['B3'] = style['D3'] =  this.getBCCStyle()
    // 模块说明内容样式
    style['A4'] = style['B4'] = style['D4'] =  this.getBCCStyle(11, '000000', false)
    // 模块说明内容样式
    style['A5'] = this.getBCCStyle(11, 'FF0000', false)
    // 数据列
    let cols = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
    // 列表头
    wsData.push(['序号', '功能描述', '地址', '请求类型', '请求格式', '响应格式', '开发者', '详细说明'])
    rowCount += 1
    // 列表头样式
    cols.forEach(key => {
      style[key + (rowCount + 1)] = this.getBCCStyle()
    })
    // 遍历接口
    data.interfaces.forEach((item, i) => {
      /* 接口信息 */
      wsData.push([
        i + 1,
        item.desc || '',
        item.url || '',
        item.method || '',
        item.request.type,
        item.response.type,
        item.developer || '',
        item.explain || ''
      ])
      rowCount += 1
      // 列表内容样式
      cols.forEach(key => {
        let cellStyle = this.getBCCStyle(11, '000000', false, 'left')
        // 名称加链接
        if (key === 'B') {
          if (inters && inters.length > 0) {
            cellStyle.l = {
              Target: '#' + inters[i].name + '!A1',
              Tooltip: '点击跳转至接口详细-' + inters[i].name
            }
            cellStyle.s.font.color = { rgb: '337ab7' }
          }
        }
        style[key + (rowCount + 1)] = cellStyle
      })
    })
    // 使用二维数组创建一个工作表对象
    const workSheet = xlsx.utils.aoa_to_sheet(wsData)
    // 列宽度
    workSheet['!cols'] = [
      {wch: 6},
      {wch: 13},
      {wch: 40},
      {wch: 13},
      {wch: 13},
      {wch: 13},
      {wch: 13},
      {wch: 60}
    ]
    // 混合配置
    this.mixinOption(workSheet, rows, merges, style)

    return workSheet
  },

  /**
   * @function 混合配置
  */
  mixinOption (workSheet, rows, merges, style) {
    // 行高度
    workSheet['!rows'] = rows
    // 单元格样式
    for (let key in style) {
      let cell = workSheet[key]
      let item = style[key]
      if (cell) {
        cell.s = item.s
        if (item.l) {
          cell.l = item.l
        }
      }
    }
    // 单元格格式
    for (let key in workSheet) {
      if (/^[A-Z]/.test(key)) {
        workSheet[key].t = 's'
      }
    }
    // 单元格合并配置
    workSheet['!merges'] = merges
  },

  /**
   * @function 获取加粗居中居中样式
   * @return {Object}
  */
  getBCCStyle (size, color, bold, horizon) {
    return {
      s: {
        font: {
          sz: size || 11,
          bold: bold === false ? bold : true,
          color: { rgb: color || '000000' }
        },
        alignment: {
          horizontal: horizon || 'center',
          vertical: 'center'
        }
      }
    }
  },

  /**
   * @function 获取加粗居左居中样式
   * @return {Object}
  */
  getBLCFStyle (fcolor, size, color, noBold, horizon) {
    let style = this.getBCCStyle(size, color, noBold, horizon || 'left')
    style.s.fill = {
      bgColor: { indexed: 64 }, fgColor: {rgb: fcolor || '99ccff'}
    }
    return style
  },

  /**
   * @function 参数行处理
   * @param {Object} node 参数对象
   * @param {Int} n 当前所在行
   * @return {Object}
  */
  getParamRow (node, n, params) {
    let type = String(node.type).toLowerCase()
    // 上一个节点
    let prevNode = params[n - 1] || {}
    let prevType = String(prevNode.type).toLowerCase()
    // 下一个节点
    let nextNode = params[n + 1] || {}
    let nextType = String(nextNode.type).toLowerCase()
    // 如果当前节点是数组，且下一节点是对象且当前节点是下一节点的父节点，类型简写为数组对象
    if (
      type === 'array' &&
      nextType === 'object' &&
      node.id === nextNode.pid
    ) {
      type = 'Array<Object>'
    }
    // 如果当前节点是对象，且上一节点是数组且当前节点是上一节点的子节点，则不渲染
    else if (
      type === 'object' &&
      prevType === 'array' &&
      node.pid === prevNode.id
    ) {
      return false
    }
    // 键名
    let name = node.name || ''
    // // 键名不存在
    // if (!/[^-]+/.test(node.name)) {
    //   return false
    // }
    // 名称加层级
    for (let i = 0; i < node.level; i++) {
      name = '├' + name
    }
    // 类型首字母大写
    type = String(type || '').split('')
    if (type[0]) {
      type[0] = type[0].toUpperCase()
    }
    type = type.join('')
    // 正则类型转换
    if (type === 'Regexp') {
      if (/^[\d]+$/.test(node.value)) {
        type = 'Number'
      }
      else {
        type = 'String'
      }
    }
    else if (type === 'Object') {
      name = name || 'object'
    }
    // 是否必需
    let required = String(node.required) === '1' ? 'Y' : 'N'
    // 是否验证
    let valid = String(node.valid) === '1' ? 'Y' : 'N'
    // 加入参数行
    return [name, type, node.value, node.desc, required, valid, node.validRule || '', node.remark || '']
  },

  /**
   * @function 获取模块对象sheet名
   * @param {Object} module 模块对象
   * @return {String}
  */
  getModuleSheetName (module) {
    let name = module.desc + '_' + module.module
    if (name.length > 21) {
      name = name.slice(0, 21)
    }
    name += '_接口列表'
    return name
  },

  /**
   * @function 获取接口对象sheet名
   * @param {Object} inter 接口对象
   * @param {Array} list 名称列表
   * @return {String}
  */
  getInterSheetInfo (inter, list) {
    let name = inter.desc + '_' + inter.interface
    if (name.length > 25) {
      name = name.slice(0, 22) + '...'
    }
    // 相同名称
    let has = false
    list.forEach(item => {
      if (item.name === name) {
        has = true
      }
    })
    if (has) {
      name = name.replace('...', '') + strer.uuid(3)
    }

    return {name}
  }
}
